#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <pthread.h>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include "InetAddr.hpp"

using std::cout;
using std::endl;

const static int _gsockfd = -1;
const static int _gport = 8888;
const static int _gbacklog = 8;

// 回调
using command_service_t = std::function<void(int sockfd, InetAddr addr)>;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    ACCEPT_ERROR
};

class TcpServer
{

public:
    TcpServer(command_service_t service,int port = _gport, const int sockfd = _gsockfd)
        :_service(service) , _listensockfd(sockfd), _port(port), _isrunning(false)
    {
    }

    void InitServer()
    {
        // 创建套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            cout << "[FATAL]: socket error!!!" << endl;
            exit(SOCKET_ERROR);
        }
        cout << "[DEBUG]: socket success->" << _listensockfd << endl;

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        socklen_t len = sizeof(local);
        // bind
        int n = ::bind(_listensockfd, (sockaddr *)&local, len);
        if (n < 0)
        {
            cout << "[FATAL]: bind error!!!" << endl;
            exit(BIND_ERROR);
        }
        cout << "[DEBUG]: bind success" << endl;
        // listen
        if (::listen(_listensockfd, _gbacklog) < 0)
        {
            cout << "[FATAL]: listen error!!!" << endl;
            exit(LISTEN_ERROR);
        }
        cout << "[DEBUG]: listen success" << endl;
    }

    struct ThreadData
    {
        ThreadData(int sockfd, InetAddr addr, TcpServer *tcp)
            : _sockfd(sockfd), _addr(addr), _tcp(tcp)
        {
        }

        int _sockfd;
        InetAddr _addr;
        TcpServer *_tcp;
    };

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // accept
            int sockfd = ::accept(_listensockfd, (sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                cout << "[FATAL]: accept error!!!" << endl;
                exit(ACCEPT_ERROR);
            }
            InetAddr addr(client);
            printf("get a new link, client info : %s, sockfd is : %d\n", addr.AddrStr().c_str(), sockfd);
            // version -3 多线程版本
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, addr, this);
            pthread_create(&tid, nullptr, Excute, td);
        }
        _isrunning = false;
    }

    static void *Excute(void *args)
    {
        // 线程分离
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->_tcp->_service(td->_sockfd, td->_addr);
        delete td;
        return nullptr;
    }
    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensockfd;
    bool _isrunning;
    command_service_t _service;
};